package org.delphy.indy2.containers
{
	import caurina.transitions.Tweener;
	
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.filters.BitmapFilter;
	import flash.filters.DropShadowFilter;
	import flash.geom.Matrix;
	import flash.geom.Point;
	
	import mx.containers.Canvas;
	import mx.core.UIComponent;
	import mx.managers.PopUpManager;
	import mx.styles.StyleManager;
	
	import org.delphy.indy2.core.ICarbonComponent;
	import org.delphy.indy2.layers.*;
	import org.delphy.indy2.containers.carbonSheetClasses.*;

	//----------------------------------
	//  Styles
	//----------------------------------	

	/**
	 *	Top-left corner radius
	 *	
	 *	@default 0
	 */
	[Style(name="topLeftRadius", type="Number", format="Length", inherit="no")]
	
	/**
	 *	Top-right corner radius
	 *	
	 *	@default 0
	 */
	[Style(name="topRightRadius", type="Number", format="Length", inherit="no")]
	
	/**
	 *	Bottom-left corner radius
	 *	
	 *	@default 0
	 */
	[Style(name="bottomLeftRadius", type="Number", format="Length", inherit="no")]
	
	/**
	 *	Bottom-right corner radius
	 *	
	 *	@default 0
	 */
	[Style(name="bottomRightRadius", type="Number", format="Length", inherit="no")]
	
	/**
	 * FilterLayer
	 * This is a dummy canvas for hosting filters 
	 * 
	 * @default: carbonPanelShadowFilter
	 */
	[Style(name="filterLayer", type="String", inherit="no")]
	
	/**
	 * Backbround Colors
	 * 
	 * @default #000000, #000000
	 */
	[Style(name="backgroundColors", type="Array", arrayType="Number", inherit="no")]

	/**
	 * Backbround Alphas
	 * 
	 * @default 0.85, 0.45
	 */
	[Style(name="backgroundAlphas", type="Array", arrayType="Number", inherit="no")]

	/**
	 * Backbround Ratios
	 */
	[Style(name="backgroundRatios", type="Array", arrayType="Number", inherit="no")]

	/**
	 * Backbround RatioUnits
	 */
	[Style(name="backgroundRatioUnits", type="Array", arrayType="String", inherit="no")]

	/**
	 * Backbround Blur Filter
	 */
	[Style(name="backgroundBlurFilter", type="String", inherit="no")]

	/**
	 * Border Colors
	 * 
	 * @default #000000, #000000
	 */
	[Style(name="borderColors", type="Array", arrayType="Number", inherit="no")]
	
	/**
	 * Border Alphas
	 */
	[Style(name="borderAlphas", type="Array", arrayType="Number", inherit="no")]
	
	/**
	 * Border Ratios
	 */
	[Style(name="borderRatios", type="Array", arrayType="Number", inherit="no")]

	/**
	 * Border RatioUnits
	 */
	[Style(name="borderRatioUnits", type="Array", arrayType="String", inherit="no")]

	/**
	 * Border Weight
	 * 
	 * @default 1
	 */
	[Style(name="borderWeight", type="Number", inherit="no")]
	
	/**
	 * Border Highlight Colors
	 * 
	 * @default #000000, #FFFFFF, #000000
	 */
	[Style(name="borderHighlightColors", type="Array", arrayType="Number", inherit="no")]
	
	/**
	 * Border Highlight Alphas
	 * 
	 * @defaut 0, 0.8, 0
	 */
	[Style(name="borderHighlightAlphas", type="Array", arrayType="Number", inherit="no")]
	
	/**
	 * Border  Highlight Ratios
	 */
	[Style(name="borderHighlightRatios", type="Array", arrayType="Number", inherit="no")]
	
	/**
	 * Border Highlight RatioUnits
	 */
	[Style(name="borderHighlightRatioUnits", type="Array", arrayType="String", inherit="no")]
	
	/**
	 * Whether to draw a horizontal highlight on border
	 * 
	 * @default true
	 */
	[Style(name="useHBorderHighlight", type="Boolean", inherit="no")]	

	/**
	 * Whether to draw a vertal highlight on border
	 * 
	 * @default true
	 */
	[Style(name="useVBorderHighlight", type="Boolean", inherit="no")]	

	public class CarbonSheet extends Canvas implements ICarbonComponent
	{
		//----------------------------------
		//  Host
		//----------------------------------	
		
		/**
		 * @protected
		 * The mask shape
		 */
		protected var hostMask:Sprite;
		
		/**
		 * @protected
		 * The mask component
		 */
		protected var hostMaskUI:UIComponent;
		
		/**
		 * @public
		 * The host object from which CarbonSheet will popup
		 */
		protected var _host:DisplayObject=null;
		public function get host():DisplayObject
		{ 
			return _host; 
		}
		
		public function set host(value:DisplayObject):void
		{
			if (value !== _host)
			{
				_host = value;
			}
		}

		//----------------------------------
		//  Geometry
		//----------------------------------
		
		/**
		 * @protected
		 * 
		 * @default 0
		 */
		protected var _topLeftRadius:Number;

		/**
		 * @protected
		 * 
		 * @default 0
		 */
		protected var _topRightRadius:Number;

		/**
		 * @protected
		 * 
		 * @default 12
		 */
		protected var _bottomLeftRadius:Number;

		/**
		 * @protected
		 * 
		 * @default 12
		 */
		protected var _bottomRightRadius:Number;

		/**
		 * @protected
		 * 
		 * @default 1
		 */
		protected var _borderWeight:Number;

		//----------------------------------
		//  Client
		//----------------------------------	
		
		/**
		 * @public
		 * The content area, which is a Canvas. All components will be added to this object
		 */
		protected var _content:Canvas;	
		public function get content():Canvas{
			return _content;
		}

		//----------------------------------
		//  Animation
		//----------------------------------	
		
		/**
		 * @public
		 * The duration of animation
		 * 
		 * @default 1.3
		 */
		public var transitionTime:Number=1.3;

		//----------------------------------
		//  Effects
		//----------------------------------	

		/**
		 * @public
		 * Whether of not to make the transparent background blur
		 * 
		 * @default true
		 */
		public var enableBackgroundBlur:Boolean = true;

		/**
		 * @protected
		 */
		protected var _filterLayer:FilterLayer;

		//----------------------------------
		//  Border Skin
		//----------------------------------			

		/** Default Skin
		 * @default org.delphy.indy2.containers.carbonSheetClasses.CarbonSheetSkin
		 */
		protected var defaultSkin:Class = CarbonSheetSkin;
		
		//----------------------------------
		//  Methods
		//----------------------------------			
		
		/**
		 * Constructor
		 */
		public function CarbonSheet(parentObject:DisplayObject=null)
		{
			super();
			visible=false;
			
			if(parentObject!=null)
				_host = parentObject;
		}
		
		/**
		 * @protected
		 * Get the geometry information
		 */
		protected function getGeometryInfo():void{
			_topLeftRadius = getStyle("topLeftRadius");
			if(!_topLeftRadius){
				_topLeftRadius = StyleManager.getStyleDeclaration(".carbonSheet").getStyle("topLeftRadius");
				setStyle("topLeftRadius", _topLeftRadius);
			}
			
			_topRightRadius = getStyle("topRightRadius");
			if(!_topRightRadius){
				_topRightRadius = StyleManager.getStyleDeclaration(".carbonSheet").getStyle("topRightRadius");
				setStyle("topRightRadius", _topRightRadius);
			}
			
			_bottomLeftRadius = getStyle("bottomLeftRadius");
			if(!_bottomLeftRadius){
				_bottomLeftRadius = StyleManager.getStyleDeclaration(".carbonSheet").getStyle("bottomLeftRadius");
				setStyle("bottomLeftRadius", _bottomLeftRadius);
			}
			
			_bottomRightRadius = getStyle("bottomRightRadius");
			if(!_bottomRightRadius){
				_bottomRightRadius = StyleManager.getStyleDeclaration(".carbonSheet").getStyle("bottomRightRadius");
				setStyle("bottomRightRadius", _bottomRightRadius);
			}
			
			_borderWeight = getStyle("borderWeight");
			if(!_borderWeight){
				_borderWeight = StyleManager.getStyleDeclaration(".carbonSheet").getStyle("borderWeight");
				setStyle("borderWeight", _borderWeight);
			}
		}
		
		/**
		 * @public
		 * Popup the CarbonSheet object. 
		 * If host is not set yet, then it will be automatically set to the parent object
		 */
		public function popUp():void{		
			if(Tweener.isTweening(this)){
				return;
			}
			
			if(_host == null)
				_host = parent;
			
			// Get the geometry information
			getGeometryInfo();
			
			// Adjust the client area
			_content.x = _borderWeight + _topLeftRadius;
			_content.y = _borderWeight + _topRightRadius;
			_content.width = width - Math.max(_topLeftRadius, _bottomLeftRadius) - Math.max(_topRightRadius, _bottomRightRadius) - _borderWeight*2;
			_content.height = height - Math.max(_topLeftRadius, _topRightRadius) - Math.max(_bottomLeftRadius, _bottomRightRadius) - _borderWeight*2;
			
			// We want to make the mask big enough to cover the dropdown shadow
			var filterArray:Array=FilterFactory.createFilters(this, "filterLayer");
			var maxShadowSize:Number=0;
			for each(var filterObject:BitmapFilter in filterArray){
				if(filterObject is DropShadowFilter){
					var shadowSize:Number=DropShadowFilter(filterObject).blurX + DropShadowFilter(filterObject).distance;
					maxShadowSize = (shadowSize > maxShadowSize) ? shadowSize : maxShadowSize; 
				}
			}

			// Determine whether to use blurred background
			if(enableBackgroundBlur)
				drawHostMask();
			
			// Initialize position
			var hostCenter:Point=new Point(_host.x,_host.y);
			hostCenter=_host.parent.localToGlobal(hostCenter);
			
			x=hostCenter.x+ (_host.width-this.width)/2;
			y=hostCenter.y-this.height;

			// Create a mask
			var aMask:Sprite=new Sprite();
			var g:Graphics=aMask.graphics;
			g.beginFill(0x000000,1);
			g.drawRect(x - maxShadowSize, hostCenter.y, width + maxShadowSize*2, height + maxShadowSize*2);
			g.endFill();
			
			this.mask=aMask;
			
			// Popup the window
			// IMPORTANT: PopUpManager will cause an exception if try to popup a child from its parent.
			//            Thus, we need to remove the child from parent first, and the pop it up.
			if(parent)
				parent.removeChild(this);
		
			PopUpManager.addPopUp(this, _host, false);			
			
			if(!visible){
				visible=true;
			}
			
			// Play animation
			Tweener.addTween(this,{y:hostCenter.y, time:transitionTime});
		}
		
		/**
		 * @public
		 * Close the window after playing the closing animation
		 */
		public function close():void{
			var hostCenter:Point=new Point(_host.x,_host.y);
			hostCenter=_host.parent.localToGlobal(hostCenter);
			
			var posY:Number=hostCenter.y-this.height;
			Tweener.addTween(this,{y:posY,time:transitionTime,onComplete:onCloseAnimationComplete});
		}
		
		/**
		 * @protected
		 * Make the spaces outside the CarbonSheet blur by drawing a mask.
		 * The mask will take over all mouse events, thus make the CarbonSheet "model".
		 */
		protected function drawHostMask():void{
			// Calculate the position
			var hostCenter:Point=new Point(_host.x,_host.y);
			hostCenter=_host.parent.localToGlobal(hostCenter);

			// Draw a "mask" by copying the bitmap data
			hostMask=new Sprite();
			
			var hostBitmap:BitmapData=new BitmapData(_host.width,_host.height,true);
			hostBitmap.draw(_host);

			var g:Graphics=hostMask.graphics;
			var mat:Matrix=new Matrix();
			mat.translate(hostCenter.x,hostCenter.y);
			g.beginBitmapFill(hostBitmap,mat);
			g.drawRect(hostCenter.x,hostCenter.y,_host.width,_host.height);
			g.endFill();
			
			g.beginFill(0x000000,0.5);
			g.drawRect(hostCenter.x,hostCenter.y,_host.width,_host.height);
			g.endFill();			
			
			// Block all mouse events
			hostMask.addEventListener(MouseEvent.CLICK,dummy);
			hostMask.addEventListener(MouseEvent.DOUBLE_CLICK,dummy);
			hostMask.addEventListener(MouseEvent.ROLL_OVER,dummy);
			hostMask.addEventListener(MouseEvent.ROLL_OUT,dummy);
			
			// Make the mask an UIComponent and apply BlurFilter to it 
			hostMaskUI=new UIComponent();
			hostMaskUI.addChild(hostMask);
			
			hostMaskUI.filters=FilterFactory.createFilters(this, "backgroundBlurFilter");
			
			// Popup the mask
			PopUpManager.addPopUp(hostMaskUI, _host, false);	
		}
		
		/**
		 * @protected
		 * Actually close the window. This method will be invoked when the Tweener finishes playing animation
		 */
		protected function onCloseAnimationComplete():void{
			if(enableBackgroundBlur)
				PopUpManager.removePopUp(hostMaskUI);
			
			PopUpManager.removePopUp(this);
		}
		
		/**
		 * @protected
		 * Simply doing nothing, thus block all mouse events
		 */
		protected function dummy(evt:MouseEvent):void{
		}

		//----------------------------------
		//  Overridden Methods
		//----------------------------------		
		
		/**
		 *  The following functions redirect requests to _content
		 */
		
		override public function addChild(child:DisplayObject):DisplayObject{
			// The first time this function is called, we maybe counter the problem that at this time, _content is null.
			// So, we need to check if it is necessery to create a _content
			if(!_content)
				_content = new Canvas();
			
			return _content.addChild(child);
		}
		
		override public function addChildAt(child:DisplayObject, index:int):DisplayObject{
			return _content.addChildAt(child,index);
		}
		
		override public function getChildAt(index:int):DisplayObject{
			return _content.getChildAt(index);
		}
		
		override public function getChildByName(name:String):DisplayObject{
			return _content.getChildByName(name);
		}
		
		override public function getChildIndex(child:DisplayObject):int{
			return _content.getChildIndex(child);
		}
		
		override public function getChildren():Array{
			return _content.getChildren();
		} 		
		
		override protected function createChildren() : void{
			super.createChildren();

			// Fallback to default skin
			if(!styleName)
				styleName="carbonSheet";
			
			if(!getStyle("borderSkin"))
				setStyle("borderSkin", defaultSkin);		
			
			// Create a client area
			if(!_content)
				_content = new Canvas();
			
			// Create a filter layer
			getGeometryInfo();
			_filterLayer=new FilterLayer(this);
			_filterLayer.setStyle("topLeftRadius", _topLeftRadius);
			_filterLayer.setStyle("topRightRadius", _topRightRadius);
			_filterLayer.setStyle("bottomLeftRadius", _bottomLeftRadius);
			_filterLayer.setStyle("bottomRightRadius", _bottomRightRadius);
			_filterLayer.initFilters();
			
			rawChildren.addChild(_filterLayer);
			rawChildren.addChild(_content);			
		}
		override public function styleChanged(styleProp:String) : void{
			super.styleChanged(styleProp);
			
			if(styleProp && styleProp.search("styleName")!=-1){
				// Refresh filterLayer
				if(_filterLayer){
					_filterLayer.initFilters(getStyle("filterLayer"));
					_filterLayer.invalidateDisplayList();
				}
			}	
		}
		
		//----------------------------------
		//  Implementation of ICarbonComponent
		//----------------------------------
		
		/**
		 * @public
		 */
		public function get defaultStyleName():String{
			return "carbonSheet";
		}
	}
}